
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;

public class ViewPanelGame extends AbstractView {
	
	public static final int BLOCK_PIXEL_WIDTH = 40;
	public static final int BLOCK_PIXEL_HEIGHT = 40;
	
	public static final int BOARD_GAME_PIXEL_HEIGHT = 590;
	
	private PanelGame panelGame;
	private ControllerGame controller;
	
	private Integer mapPositionX;
	private Integer mapPositionY;
	
	private long animationClock;
	private long animationClockRound;
	private long animationClockAttack;
	private long animationClockDestroyed;
	private boolean isAnimationSectionMove;
	private boolean isAnimationSectionRound;
	private boolean isAnimationSectionAttack;
	private boolean isAnimationSectionDestroyed;
	
	private boolean isMenuOn;
	private int menuItemID;
	
	private ArrayList<Point> movePath;
	private int moveIter;
	
	private boolean isDestSet;
	
	private Integer round;
	private Point attackedUnitCoord;
	private Point destroyedUnitCoord;
	
	/*
	 * Copies of all necessary objects within the model of panel game
	 */
	private Map map;
	private ArrayList<Point> reachableArea;
	private ArrayList<Point> attackableArea;
	private ArrayList<Point> path;
	
	/*
	 * Inner class PanelGame
	 */
	public class PanelGame extends JPanel implements MouseMotionListener, MouseListener, KeyListener {
		
		private static final long serialVersionUID = 1L;
		private Image iBuffer;	// Used for double buffering
		private Graphics2D gBuffer;	// Used for double buffering
		
		/*
		 * Key state flags for moving map
		 */
		private boolean keyState_up, keyState_down;
		private boolean keyState_left, keyState_right;
		
		/*
		 * Inner class PanelGame constructor
		 */
		public PanelGame() {
			setLayout(null);
			addButtons();
			addKeyListener(this);
			addMouseMotionListener(this);
			addMouseListener(this);
			keyState_up = false;
			keyState_down = false;
			keyState_left = false;
			keyState_right = false;
			
			Thread mapMoveCtrl = new Thread(new MapMoveCtrl());
			mapMoveCtrl.start();
			Thread repaint = new Thread(new Repaint(this));
			repaint.start();
		}

		public void paintComponent(Graphics g) {
			if(iBuffer == null) {
				iBuffer = createImage(this.getSize().width, this.getSize().height);
				gBuffer = (Graphics2D) iBuffer.getGraphics();
			}
			
			/*
			 * Erase previous image in background
			 */
			gBuffer.setColor(getBackground());
			gBuffer.fillRect(0, 0, this.getSize().width, this.getSize().height);
			
			/*
			 * Area assigned for game board
			 */
			gBuffer.setColor(Color.GRAY);
			gBuffer.fillRect(0, 0, panelGame.getWidth(), BOARD_GAME_PIXEL_HEIGHT);
			
			if(map == null)
				return;
			
			/*
			 * Draw map
			 */
			int blockIndex = 0;
			while(blockIndex < map.getBlocksList().size()) {
				BufferedImage blockImage = null;
				if(map.getBlocksList().get(blockIndex).getBlockTypeID() == Block.BLOCK_TYPE_ID_FIELD)
					blockImage = WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BLOCK_FIELD_1);
				else if(map.getBlocksList().get(blockIndex).getBlockTypeID() == Block.BLOCK_TYPE_ID_FOREST)
					blockImage = WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BLOCK_FOREST);
				else if(map.getBlocksList().get(blockIndex).getBlockTypeID() == Block.BLOCK_TYPE_ID_MIRE)
					blockImage = WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BLOCK_MIRE);
				else if(map.getBlocksList().get(blockIndex).getBlockTypeID() == Block.BLOCK_TYPE_ID_MOUNTAIN)
					blockImage = WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BLOCK_MOUNTAIN);
				else if(map.getBlocksList().get(blockIndex).getBlockTypeID() == Block.BLOCK_TYPE_ID_ROAD)
					blockImage = WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BLOCK_ROAD_BASE);
				else if(map.getBlocksList().get(blockIndex).getBlockTypeID() == Block.BLOCK_TYPE_ID_WATER) {
					int waterAnimationClock = (int) (animationClock % 300);
					if(waterAnimationClock < 100)
						blockImage = WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BLOCK_WATER_1);
					else if(waterAnimationClock >= 100 && waterAnimationClock < 200)
						blockImage = WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BLOCK_WATER_2);
					else if(waterAnimationClock >= 200 && waterAnimationClock < 300)
						blockImage = WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BLOCK_WATER_3);
				}
				
        		gBuffer.drawImage(blockImage,
        				map.getBlocksList().get(blockIndex).getBlockCoord().x * BLOCK_PIXEL_WIDTH + mapPositionX,
        				map.getBlocksList().get(blockIndex).getBlockCoord().y * BLOCK_PIXEL_HEIGHT + mapPositionY, 
        				BLOCK_PIXEL_WIDTH, 
        				BLOCK_PIXEL_HEIGHT,
        				this);	// Draw single Block
        		blockIndex++;
			}
			
			/*
			 * Draw grids
			 */
			/*
	        for(int row = 0; row < map.getHeight(); row++) {
	        	for(int column = 0; column < map.getWidth(); column++) {
	        		//if()
	        		
	        		gBuffer.setColor(Color.GREEN);
	        		gBuffer.drawRect(column * BLOCK_PIXEL_WIDTH + mapPositionX,
	        				row * BLOCK_PIXEL_HEIGHT + mapPositionY, 
	        				BLOCK_PIXEL_WIDTH, 
	        				BLOCK_PIXEL_HEIGHT);	// Draw single Block
	        	}
	        }
	        */
	        
	        /*
	         * Draw units
	         */
			int unitIndex = 0;
			while(unitIndex < map.getUnitsList().size()) {
				if(isAnimationSectionMove && map.getUnitsList().get(unitIndex).isSelected()) {
					unitIndex++;
					continue;
				}
				
        		gBuffer.drawImage(map.getUnitsList().get(unitIndex).getUnitImage(),
        				(int) map.getUnitsList().get(unitIndex).getUnitCoord().getX() * BLOCK_PIXEL_WIDTH + mapPositionX,
        				(int) map.getUnitsList().get(unitIndex).getUnitCoord().getY() * BLOCK_PIXEL_HEIGHT + mapPositionY, 
        				BLOCK_PIXEL_WIDTH, 
        				BLOCK_PIXEL_HEIGHT,
        				this);
        		unitIndex++;
			}
			
			/*
			 * Highlight active units with box
			 */
			for(int i = 0; i < map.getUnitsList().size(); i++) {
		        if(map.getUnitsList().get(i).isActive() && !isAnimationSectionMove) {
		        	int clock = (int) (animationClock % 80);
		        	Point coord = map.getUnitsList().get(i).getUnitCoord();
		        	gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_HIGHLIGHT_UNIT_1),
	        				coord.x * BLOCK_PIXEL_WIDTH + mapPositionX,
	        				coord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, 
	        				BLOCK_PIXEL_WIDTH, 
	        				BLOCK_PIXEL_HEIGHT,
	        				this);
		        	
		        	/*
		        	 * Increase or decrease the contrast of the box with given clock
		        	 */
		        	if(clock < 40)
			        	for(int j = 0; j < clock; j++) {
			        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_HIGHLIGHT_UNIT_2),
			        				coord.x * BLOCK_PIXEL_WIDTH + mapPositionX,
			        				coord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, 
			        				BLOCK_PIXEL_WIDTH, 
			        				BLOCK_PIXEL_HEIGHT,
			        				this);
			        	}
		        	else if(clock >= 40)
		        		for(int j = 0; j < 80 - clock; j++) {
			        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_HIGHLIGHT_UNIT_2),
			        				coord.x * BLOCK_PIXEL_WIDTH + mapPositionX,
			        				coord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, 
			        				BLOCK_PIXEL_WIDTH, 
			        				BLOCK_PIXEL_HEIGHT,
			        				this);
			        	}
		        }
			}
	        
	        /*
	         * Highlight selected units
	         */
			for(int i = 0; i < map.getUnitsList().size(); i++) {
		        if(map.getUnitsList().get(i).isSelected() && !isAnimationSectionMove) {
		        	Point coord = map.getUnitsList().get(i).getUnitCoord();
		        	gBuffer.setColor(Color.RED);
		        	Point p1 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 - 4 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT - 5 + mapPositionY);
    				Point p2 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + 4 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT - 5 + mapPositionY);
    				Point p3 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + mapPositionY);
    				int[] xs = { p1.x, p2.x, p3.x };
    				int[] ys = { p1.y, p2.y, p3.y };
    				Polygon triangle = new Polygon(xs, ys, xs.length);
    				gBuffer.fillPolygon(triangle);
		        	break;
		        }
			}
	        
	        /*
	         * Draw reachable area
	         */
	        if(reachableArea != null) {
	        	int reachableBlockIndex = 0;
	        	gBuffer.setColor(new Color(0.1f, 0.1f, 0.1f, 0.5f));
	        	
	        	while(reachableBlockIndex < reachableArea.size()) {
	        		gBuffer.fillRect((int) reachableArea.get(reachableBlockIndex).getX() * BLOCK_PIXEL_WIDTH + mapPositionX, 
	        				(int) reachableArea.get(reachableBlockIndex).getY() * BLOCK_PIXEL_HEIGHT + mapPositionY, 
	        				BLOCK_PIXEL_WIDTH, 
	        				BLOCK_PIXEL_HEIGHT);
	        		reachableBlockIndex++;
	        	}
	        }
	        
	        /*
	         * Draw path
	         */
	        if(path != null) {
	        	int pathBlockIndex = 0;
	        	gBuffer.setColor(Color.CYAN);
	        	
	        	while(pathBlockIndex < path.size()) {
	        		if(pathBlockIndex == 0) {
	        			pathBlockIndex++;
	        			continue;
	        		}
	        		
	        		if(pathBlockIndex == path.size() - 1) {
	        			Point prevCoord = path.get(pathBlockIndex - 1);
	        			Point coord = path.get(pathBlockIndex);
	        			if(prevCoord.x == coord.x - 1 && prevCoord.y == coord.y) {	// >
	        				Point p1 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 - 8 + mapPositionY);
	        				Point p2 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + 8 + mapPositionY);
	        				Point p3 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + 5 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + mapPositionY);
	        				int[] xs = { p1.x, p2.x, p3.x };
	        				int[] ys = { p1.y, p2.y, p3.y };
	        				Polygon triangle = new Polygon(xs, ys, xs.length);
	        				gBuffer.fillPolygon(triangle);
	        				gBuffer.fillRect(coord.x * BLOCK_PIXEL_WIDTH + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 - 4 + mapPositionY, BLOCK_PIXEL_WIDTH / 2, 4 * 2);
	        			}
	        			else if(prevCoord.x == coord.x + 1 && prevCoord.y == coord.y) {	// <
	        				Point p1 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 - 8 + mapPositionY);
	        				Point p2 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + 8 + mapPositionY);
	        				Point p3 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 - 5 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + mapPositionY);
	        				int[] xs = { p1.x, p2.x, p3.x };
	        				int[] ys = { p1.y, p2.y, p3.y };
	        				Polygon triangle = new Polygon(xs, ys, xs.length);
	        				gBuffer.fillPolygon(triangle);
	        				gBuffer.fillRect(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 - 4 + mapPositionY, BLOCK_PIXEL_WIDTH / 2, 4 * 2);
	        			}
	        			else if(prevCoord.x == coord.x && prevCoord.y == coord.y - 1) {	// v
	        				Point p1 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 - 8 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + mapPositionY);
	        				Point p2 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + 8 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + mapPositionY);
	        				Point p3 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + 5 + mapPositionY);
	        				int[] xs = { p1.x, p2.x, p3.x };
	        				int[] ys = { p1.y, p2.y, p3.y };
	        				Polygon triangle = new Polygon(xs, ys, xs.length);
	        				gBuffer.fillPolygon(triangle);
	        				gBuffer.fillRect(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 - 4 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, 4 * 2, BLOCK_PIXEL_HEIGHT / 2);
	        			}
	        			else if(prevCoord.x == coord.x && prevCoord.y == coord.y + 1) {	// ^
	        				Point p1 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 - 8 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + mapPositionY);
	        				Point p2 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + 8 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + mapPositionY);
	        				Point p3 = new Point(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 - 5 + mapPositionY);
	        				int[] xs = { p1.x, p2.x, p3.x };
	        				int[] ys = { p1.y, p2.y, p3.y };
	        				Polygon triangle = new Polygon(xs, ys, xs.length);
	        				gBuffer.fillPolygon(triangle);
	        				gBuffer.fillRect(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 - 4 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + mapPositionY, 4 * 2, BLOCK_PIXEL_HEIGHT / 2);
	        			}
	        		}
	        		else {
	        			Point prevCoord = path.get(pathBlockIndex - 1);
	        			Point coord = path.get(pathBlockIndex);
	        			Point nxtCoord = path.get(pathBlockIndex + 1);
	        			gBuffer.fillRect(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 - 4 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 - 4 + mapPositionY, 4 * 2, 4 * 2);
	        			if(prevCoord.x == coord.x && prevCoord.y == coord.y - 1 || nxtCoord.x == coord.x && nxtCoord.y == coord.y - 1) {	// from up
	        				gBuffer.fillRect(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 - 4 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, 4 * 2, BLOCK_PIXEL_HEIGHT / 2 - 4);
	        			}
	        			if(prevCoord.x == coord.x && prevCoord.y == coord.y + 1 || nxtCoord.x == coord.x && nxtCoord.y == coord.y + 1) {	// from down
	        				gBuffer.fillRect(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 - 4 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 + 4 + mapPositionY, 4 * 2, BLOCK_PIXEL_HEIGHT / 2 - 4);
	        			}
	        			if(prevCoord.x == coord.x - 1 && prevCoord.y == coord.y || nxtCoord.x == coord.x - 1 && nxtCoord.y == coord.y) {	// from left
	        				gBuffer.fillRect(coord.x * BLOCK_PIXEL_WIDTH + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 - 4 + mapPositionY, BLOCK_PIXEL_WIDTH / 2 - 4, 4 * 2);
	        			}
	        			if(prevCoord.x == coord.x + 1 && prevCoord.y == coord.y || nxtCoord.x == coord.x + 1 && nxtCoord.y == coord.y) {	// from right
	        				gBuffer.fillRect(coord.x * BLOCK_PIXEL_WIDTH + BLOCK_PIXEL_WIDTH / 2 + 4 + mapPositionX, coord.y * BLOCK_PIXEL_HEIGHT + BLOCK_PIXEL_HEIGHT / 2 - 4 + mapPositionY, BLOCK_PIXEL_WIDTH / 2 - 4, 4 * 2);
	        			}
	        		}
	        		
	        		/*
	        		gBuffer.drawRect((int) path.get(pathBlockIndex).getX() * BLOCK_PIXEL_WIDTH + mapPositionX, 
	        				(int) path.get(pathBlockIndex).getY() * BLOCK_PIXEL_HEIGHT + mapPositionY, 
	        				BLOCK_PIXEL_WIDTH, 
	        				BLOCK_PIXEL_HEIGHT);
	        		*/
	        		pathBlockIndex++;	        		
	        	}
	        }
	        
	        /*
	         * Draw attackable area
	         */
	        if(attackableArea != null && !isAnimationSectionMove) {
	        	int attackableBlockIndex = 0;
	        	gBuffer.setColor(Color.RED);
	        	
	        	while(attackableBlockIndex < attackableArea.size()) {
	        		gBuffer.drawRect(attackableArea.get(attackableBlockIndex).x * BLOCK_PIXEL_WIDTH + mapPositionX, 
	        				attackableArea.get(attackableBlockIndex).y * BLOCK_PIXEL_HEIGHT + mapPositionY, 
	        				BLOCK_PIXEL_WIDTH, 
	        				BLOCK_PIXEL_HEIGHT);
	        		attackableBlockIndex++;
	        	}
	        }
	        
	        /*
	         * Unit move animation
	         */
	        if(isAnimationSectionMove) {
	        	Unit selectedUnit = null;
    			for(int i = 0; i < map.getUnitsList().size(); i++) {
    				if(map.getUnitsList().get(i).isSelected()) {
    					selectedUnit = map.getUnitsList().get(i);
    					break;
    				}
    			}
    			gBuffer.drawImage(selectedUnit.getUnitImage(),
        				movePath.get(moveIter).x + mapPositionX,
        				movePath.get(moveIter).y + mapPositionY, 
        				BLOCK_PIXEL_WIDTH, 
        				BLOCK_PIXEL_HEIGHT,
        				this);
        		
        		if(animationClock % 1 == 0) {
        			moveIter += 2;
        		}
        		
        		if(moveIter >= movePath.size()) {
        			isAnimationSectionMove = false;
        		}
	        }	        
			
			/*
			 * Area assigned for information board
			 */
			gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BOARD_INFO), 0, 590, this.getWidth(), 82, this);
			
			/*
        	 * Print round info
        	 */
			gBuffer.setColor(Color.DARK_GRAY);
			gBuffer.setFont(new Font("Kristen ITC", Font.BOLD, 10));
			gBuffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			gBuffer.drawString("Round:", 630, 615);
        	gBuffer.drawString("Side 1", 630, 639);
        	gBuffer.drawString("Side 2", 630, 654);
        	if(round != null) {
        		gBuffer.drawString(round.toString(), 673, 615);
        		
        		gBuffer.setColor(Color.RED);
        		Point p1 = null, p2 = null, p3 = null;
        		if(round % 2 == 1) {
        			p1 = new Point(615, 635 + 4);
        			p2 = new Point(615, 635 - 4);
        			p3 = new Point(625, 635);
        		}
        		else if(round % 2 == 0) {
        			p1 = new Point(615, 650 + 4);
        			p2 = new Point(615, 650 - 4);
        			p3 = new Point(625, 650);
        		}
				int[] xs = { p1.x, p2.x, p3.x };
				int[] ys = { p1.y, p2.y, p3.y };
				Polygon triangle = new Polygon(xs, ys, xs.length);
				gBuffer.fillPolygon(triangle);
        	}
			
			/*
			 * Print information of the selected unit
			 */
			gBuffer.setColor(Color.DARK_GRAY);
			gBuffer.setFont(new Font("Kristen ITC", Font.BOLD, 10));
			gBuffer.drawString("Health:", 279, 612);
        	gBuffer.drawString("Damage:", 360, 612);
        	gBuffer.drawString("Motion:", 440, 612);
			
			for(int i = 0; i < map.getUnitsList().size(); i++) {
		        if(map.getUnitsList().get(i).isSelected) {
		        	gBuffer.drawImage(map.getUnitsList().get(i).getUnitImage(),	18, 608, 50, 50, this);
		        	gBuffer.drawString(map.getUnitsList().get(i).getUnitName(), 90, 612);
		        	
		        	gBuffer.setFont(new Font("Kristen ITC", Font.PLAIN, 10));
		        	
		        	/*
		        	 * Print unit description
		        	 */
		        	String description = map.getUnitsList().get(i).getUnitDescription();
		        	String subDescription = null;
		        	int maxLength = 32;
		        	int rowNum = map.getUnitsList().get(i).getUnitDescription().length() / maxLength + 1;
		        	int rowCount = 0;
		        	int charHeight = 630;
		        	while(rowCount < rowNum) {
		        		if(rowCount != rowNum - 1) {
		        			subDescription = description.substring(rowCount * maxLength, rowCount * maxLength + maxLength);
		        		}
		        		else {
		        			subDescription = description.substring(rowCount * maxLength);
		        		}
		        		gBuffer.drawString(subDescription, 85, charHeight);
		        		charHeight += 12;
		        		rowCount++;
		        	}
		        	
		        	/*
		        	 * Print unit HP, Damage and Motion
		        	 */
		        	gBuffer.drawString(map.getUnitsList().get(i).getRemainingHitPoint()
		        			+ "/" + map.getUnitsList().get(i).getHitPoint(), 286, 640);
		        	gBuffer.drawString(map.getUnitsList().get(i).getAttackPower() + "", 380, 640);
		        	gBuffer.drawString(map.getUnitsList().get(i).getMotionPoint() + "", 463, 640);
		        	
		        	break;
		        }
			}
			
			/*
	         * Attack animation
	         */
	        if(isAnimationSectionAttack) {
	        	if(animationClockAttack >= 80) {
	        		animationClockAttack = 0;
	        		isAnimationSectionAttack = false;
				}
	        	
	        	if(animationClockAttack > 0 && animationClockAttack < 10)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_EXPLOSION_1), attackedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, attackedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT, this);
	        	else if(animationClockAttack >= 10 && animationClockAttack < 20) 
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_EXPLOSION_2), attackedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, attackedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT, this);
	        	else if(animationClockAttack >= 20 && animationClockAttack < 30)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_EXPLOSION_3), attackedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, attackedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT, this);
	        	else if(animationClockAttack >= 30 && animationClockAttack < 40)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_EXPLOSION_4), attackedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, attackedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT, this);
	        	else if(animationClockAttack >= 40 && animationClockAttack < 50)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_EXPLOSION_5), attackedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, attackedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT, this);
	        	else if(animationClockAttack >= 50 && animationClockAttack < 60)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_EXPLOSION_6), attackedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, attackedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT, this);
	        	else if(animationClockAttack >= 60 && animationClockAttack < 70)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_EXPLOSION_7), attackedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, attackedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT, this);
	        	else if(animationClockAttack >= 70 && animationClockAttack < 80)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_EXPLOSION_8), attackedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, attackedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT, this);
	        	
	        	animationClockAttack++;
	        }
	        
	        /*
	         * Unit destroyed animation
	         */
	        if(isAnimationSectionDestroyed) {
	        	if(animationClockDestroyed >= 170) {
	        		animationClockDestroyed = 0;
	        		isAnimationSectionDestroyed = false;
				}
	        	
	        	if(animationClockDestroyed > 0 && animationClockDestroyed < 10)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_1), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);
	        	else if(animationClockDestroyed >= 10 && animationClockDestroyed < 20)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_2), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);
	        	else if(animationClockDestroyed >= 20 && animationClockDestroyed < 30)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_3), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 30 && animationClockDestroyed < 40)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_4), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 40 && animationClockDestroyed < 50)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_5), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 50 && animationClockDestroyed < 60)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_6), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 60 && animationClockDestroyed < 70)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_7), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 70 && animationClockDestroyed < 80)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_8), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 80 && animationClockDestroyed < 90)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_9), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 90 && animationClockDestroyed < 100)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_10), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 100 && animationClockDestroyed < 110)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_11), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 110 && animationClockDestroyed < 120)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_12), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 120 && animationClockDestroyed < 130)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_13), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 130 && animationClockDestroyed < 140)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_14), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 140 && animationClockDestroyed < 150)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_15), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 150 && animationClockDestroyed < 160)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_16), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	else if(animationClockDestroyed >= 160 && animationClockDestroyed < 170)
	        		gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_DESTROYED_17), destroyedUnitCoord.x * BLOCK_PIXEL_WIDTH + mapPositionX, destroyedUnitCoord.y * BLOCK_PIXEL_HEIGHT + mapPositionY, BLOCK_PIXEL_WIDTH, BLOCK_PIXEL_HEIGHT,this);	
	        	
	        	animationClockDestroyed++;
	        }
			
			/*
			 * Next round animation
			 */
			if(isAnimationSectionRound) {
				if(animationClockRound >= 200) {
					animationClockRound = 0;
					isAnimationSectionRound = false;
				}
				
				if(animationClockRound >= 0 && animationClockRound <= 30) {
					/*
					 * Slide up
					 */
					gBuffer.setColor(Color.DARK_GRAY);
					gBuffer.fillRect(0, (int) (300 - animationClockRound * 3), this.getWidth(), 5);
					gBuffer.fillRect(0, (int) (300 + animationClockRound * 3), this.getWidth(), 5);
					gBuffer.setColor(new Color(0.1f, 0.1f, 0.1f, 0.5f));
					gBuffer.fillRect(0, (int) (300 - animationClockRound * 3 + 5), this.getWidth(), (int) (animationClockRound * 3 * 2 - 5));
				}
				else if(animationClockRound > 30 && animationClockRound < 170) {
					gBuffer.setColor(Color.DARK_GRAY);
					gBuffer.fillRect(0, 300 - 30 * 3, this.getWidth(), 5);
					gBuffer.fillRect(0, 300 + 30 * 3, this.getWidth(), 5);
					gBuffer.setColor(new Color(0.1f, 0.1f, 0.1f, 0.5f));
					gBuffer.fillRect(0, 300 - 30 * 3 + 5, this.getWidth(), 30 * 3 * 2 - 5);
					
					/*
					 * Round info flies in
					 */
					gBuffer.setColor(Color.LIGHT_GRAY);
					gBuffer.setFont(new Font("Kristen ITC", Font.BOLD, 30));
					gBuffer.drawString("Round: " + round.toString(), 330, 310);
				}
				else if(animationClockRound >= 170 && animationClockRound < 200) {
					/*
					 * Slide down
					 */
					gBuffer.setColor(Color.DARK_GRAY);
					gBuffer.fillRect(0, (int) (300 - 30 * 3 + (animationClockRound - 170) * 3), this.getWidth(), 5);
					gBuffer.fillRect(0, (int) (300 + 30 * 3 - (animationClockRound - 170) * 3), this.getWidth(), 5);
					gBuffer.setColor(new Color(0.1f, 0.1f, 0.1f, 0.5f));
					gBuffer.fillRect(0, (int) (300 - 30 * 3 + (animationClockRound - 170) * 3 + 5), this.getWidth(), (int) ((200 - animationClockRound) * 3 * 2 - 5));
				}
				animationClockRound++;
			}
			
			/*
			 * Print Menu
			 */
			if(isMenuOn) {
				gBuffer.setColor(new Color(0.1f, 0.1f, 0.1f, 0.5f));
				gBuffer.fillRect(0, 0, this.getWidth(), this.getHeight());
				if(menuItemID == 0)
					gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_MENU_1), 
							this.getWidth() / 2 - 180, 140, 180 * 2, 310, this);
				else if(menuItemID == 1)
					gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_MENU_2), 
							this.getWidth() / 2 - 180, 140, 180 * 2, 310, this);
				else if(menuItemID == 2)
					gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_MENU_3), 
							this.getWidth() / 2 - 180, 140, 180 * 2, 310, this);
				else if(menuItemID == 3)
					gBuffer.drawImage(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_MENU_4), 
							this.getWidth() / 2 - 180, 140, 180 * 2, 310, this);
				
				//gBuffer.fillRect(this.getWidth() / 2 - 126, 236, 126 * 2, 45);
				//gBuffer.fillRect(this.getWidth() / 2 - 126, 236 + 58, 126 * 2, 45);
				//gBuffer.fillRect(this.getWidth() / 2 - 126, 236 + 58 * 2, 126 * 2, 45);
			}
	        
		    /*
		     * Print image on screen
		     */
			animationClock++;
			g.drawImage(iBuffer, 0, 0, this);
		}
		
		/**********************************************************************************************************************************************************
		 **********************************************************************************************************************************************************/
		
		/*
		 * Add buttons
		 */
		private void addButtons() {
			/*
			 * Clear the panel
			 */
			this.removeAll();
			
			/*
	         * Initialize the buttons
	         */
			ImageIcon iconEndTurn = new ImageIcon(WorldWar.imageLoader.getImageByName(ImageStructure.IMAGE_NAME_BUTTON_END_TURN));
	        JButton buttonEndTurn = new JButton(iconEndTurn);
	        buttonEndTurn.setBounds(694, 604, 90, 54);
	        
	        /*
	         * Add ActionListener to buttons
	         */
	        buttonEndTurn.addActionListener(new ActionListener() {
	            public void actionPerformed(ActionEvent evt) {
	            	controller.buttonPressedEndTurn();
	            	panelGame.requestFocusInWindow();
	            }
	        });
	        
	        /*
	         * Add buttons to the menu
	         */
	        this.add(buttonEndTurn);
	        
	        /*
			 * Refresh
			 */
	        this.repaint();
		}
		
		/*
		 * Implement of MouseMotionListener
		 */
		public void mouseMoved(MouseEvent evt) {
			if(isMenuOn) {				
				if(evt.getX() >= this.getWidth() / 2 - 126 && evt.getX() <= this.getWidth() / 2 + 126 * 2) {
					if(evt.getY() >= 236 && evt.getY() <= 236 + 45)
						menuItemID = 1;
					else if(evt.getY() >= 236 + 58 && evt.getY() <= 236 + 45 + 58)
						menuItemID = 2;
					else if(evt.getY() >= 236 + 58 * 2 && evt.getY() <= 236 + 45 + 58 * 2)
						menuItemID = 3;
					else
						menuItemID = 0;
				}
				else
					menuItemID = 0;
				return;
			}
			
			if(reachableArea == null || reachableArea.size() == 0) {			
				return;
			}
			
			if(evt.getX() >= 0 && 
					evt.getX() <= panelGame.getWidth() &&
					evt.getY() >= 0 &&
					evt.getY() <= BOARD_GAME_PIXEL_HEIGHT) {
				Integer coordX = (evt.getX() - mapPositionX) / BLOCK_PIXEL_WIDTH;
				Integer coordY = (evt.getY() - mapPositionY) / BLOCK_PIXEL_HEIGHT;
				Point coord = new Point(coordX, coordY);
				controller.mouseMoveInsideBoardGame(coord);
			}
		}
		
		public void mouseDragged(MouseEvent evt) {
			// Do nothing
		}

		/*
		 * Implement of MouseListener
		 */
		public void mouseClicked(MouseEvent evt) {
			// Do nothing
		}

		public void mouseEntered(MouseEvent evt) {
			// Do nothing	
		}

		public void mouseExited(MouseEvent evt) {
			// Do nothing	
		}

		public void mousePressed(MouseEvent evt) {
			// Do nothing	
		}

		public void mouseReleased(MouseEvent evt) {
			if(isMenuOn) {				
				if(evt.getX() >= this.getWidth() / 2 - 126 && evt.getX() <= this.getWidth() / 2 + 126 * 2) {
					if(evt.getY() >= 236 && evt.getY() <= 236 + 45)
						isMenuOn = false;
					else if(evt.getY() >= 236 + 58 && evt.getY() <= 236 + 45 + 58) {
						/*
						 * Reset panel
						 */
						resetPanelGame();						
						controller.buttonPressedReturnToMenu();
					}
					else if(evt.getY() >= 236 + 58 * 2 && evt.getY() <= 236 + 45 + 58 * 2) {						
						controller.buttonPressedExitToWindows();
					}
				}
				return;
			}
			
			/*
			 * Reture if animation is on
			 */
			if(isAnimationSectionMove)
				return;
			
			/*
			 * Mouse clicked inside the play board
			 */
			Integer coordX = (evt.getX() - mapPositionX) / BLOCK_PIXEL_WIDTH;
			Integer coordY = (evt.getY() - mapPositionY) / BLOCK_PIXEL_HEIGHT;
			Point coord = new Point(coordX, coordY);
			if(evt.getButton() == MouseEvent.BUTTON1) {	// Left button
				if(evt.getX() >= 0 && 
						evt.getX() <= panelGame.getWidth() &&
						evt.getY() >= 0 &&
						evt.getY() <= BOARD_GAME_PIXEL_HEIGHT) {
					controller.mouseLeftButtonClickedInsideBoardGame(coord);
				}
			}
			else if(evt.getButton() == MouseEvent.BUTTON3) {	// Right button
				if(evt.getX() >= 0 && 
						evt.getX() <= panelGame.getWidth() &&
						evt.getY() >= 0 &&
						evt.getY() <= BOARD_GAME_PIXEL_HEIGHT) {
					controller.mouseRightButtonClickedInsideBoardGame(coord);
				}
			}
			
			/*
			 * Mouse clicked inside the information board
			 */
		}

		/*
		 * Implement of KeyListener
		 */
		public void keyPressed(KeyEvent keyEvt) {
			if(keyEvt.getKeyCode() == KeyEvent.VK_UP) {
				keyState_up = true;
			}
			else if(keyEvt.getKeyCode() == KeyEvent.VK_DOWN) {
				keyState_down = true;
			}

			if(keyEvt.getKeyCode() == KeyEvent.VK_LEFT) {
				keyState_left = true;
			}
			else if(keyEvt.getKeyCode() == KeyEvent.VK_RIGHT) {
				keyState_right = true;
			}
		}

		public void keyReleased(KeyEvent keyEvt) {
			if(keyEvt.getKeyCode() == KeyEvent.VK_UP) {
				keyState_up = false;
			}
			else if(keyEvt.getKeyCode() == KeyEvent.VK_DOWN) {
				keyState_down = false;
			}

			if(keyEvt.getKeyCode() == KeyEvent.VK_LEFT) {
				keyState_left = false;
			}
			else if(keyEvt.getKeyCode() == KeyEvent.VK_RIGHT) {
				keyState_right = false;
			}
			else if(keyEvt.getKeyCode() == KeyEvent.VK_ESCAPE) {
				if(isMenuOn)
					isMenuOn = false;
				else if(!isMenuOn)
					isMenuOn = true;
			}
		}

		public void keyTyped(KeyEvent arg0) {
			// Do nothing
		}
		
		/*
		 * Thread to update the display area of the map
		 */
		public class MapMoveCtrl implements Runnable {
			
			public void run() {
				while(true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException ex) {
						ex.printStackTrace();
					}
					if(map == null)
						continue;
					
					/*
					 * The case that map is smaller than the game board
					 */
					Integer smallerFlag = isMapSmallerThanBoard();
					if(smallerFlag == 0) {
						// Do nothing
					}
					else if(smallerFlag == 1) {	// Width is smaller
						mapPositionX = (panelGame.getWidth() - map.getWidth() * BLOCK_PIXEL_WIDTH) / 2;
						continue;
					}
					else if(smallerFlag == 2) {	// Height is smaller
						mapPositionY = (BOARD_GAME_PIXEL_HEIGHT - map.getHeight() * BLOCK_PIXEL_HEIGHT) / 2;
						continue;
					}
					else if(smallerFlag == 3) {	// Height is smaller
						mapPositionX = (panelGame.getWidth() - map.getWidth() * BLOCK_PIXEL_WIDTH) / 2;
						mapPositionY = (BOARD_GAME_PIXEL_HEIGHT - map.getHeight() * BLOCK_PIXEL_HEIGHT) / 2;
						continue;
					}
					
					if(keyState_up)
						mapPositionY += 1;
					else if(keyState_down) 
						mapPositionY -= 1;
					if(keyState_left)
						mapPositionX += 1;
					else if(keyState_right)
						mapPositionX -= 1;
					
					if(mapPositionX > 0)
						mapPositionX = 0;
					else if(mapPositionX < panelGame.getWidth() - BLOCK_PIXEL_WIDTH * map.getWidth() - 1)
						mapPositionX = panelGame.getWidth() - BLOCK_PIXEL_WIDTH * map.getWidth() - 1;
					
					if(mapPositionY > 0)
						mapPositionY = 0;
					else if(mapPositionY < BOARD_GAME_PIXEL_HEIGHT - BLOCK_PIXEL_HEIGHT * map.getHeight() - 1)
						mapPositionY = BOARD_GAME_PIXEL_HEIGHT - BLOCK_PIXEL_HEIGHT * map.getHeight() - 1;
				}
			}
		}
		
		/*
		 * Thread to repaint panelGame periodically
		 */
		public class Repaint implements Runnable {
			
			private PanelGame panel;
			
			public Repaint(PanelGame pG){
				panel = pG;
			}
			
			/*
			 * 10ms interval between two repaints
			 */
			public void run() {
				while(true) {
					panel.repaint();
					try {
						Thread.sleep(10);
					} catch (InterruptedException ex) {
						ex.printStackTrace();
					}
				}
			}
		}
	}
	
	/*
	 * Constructor
	 */
	public ViewPanelGame() {
		resetPanelGame();
		panelGame = new PanelGame();
	}
	
	/*
	 * Reset panel
	 */
	public void resetPanelGame() {
		isDestSet = false;
		isAnimationSectionMove = false;
		isAnimationSectionRound = false;
		isAnimationSectionAttack = false;
		animationClock = 0;
		animationClockRound = 0;
		animationClockAttack = 0;
		animationClockDestroyed = 0;
		mapPositionX = 0;
		mapPositionY = 0;
		isMenuOn = false;
		menuItemID = 0;
	}
	
	/*
	 * Mutators
	 */
	public void setController(ControllerGame ctrl) {
		controller = ctrl;
	}
	
	/*
	 * Accessors
	 */
	public JPanel getPanel() {
		return panelGame;
	}
	
	public ControllerGame getController() {
		return controller;
	}
	
	/*
	 * Check if the width and height is smaller than the game board
	 * return		if
	 * 0			fine
	 * 1			only map width is smaller
	 * 2			only map height is smaller
	 * 3			both map height and width are smaller
	 */
	private Integer isMapSmallerThanBoard() {
		if(map == null)
			return 0;
		
		Integer flag = 0;
		if(map.getWidth() * BLOCK_PIXEL_WIDTH <= panelGame.getWidth())
			flag += 1;
		if(map.getHeight() * BLOCK_PIXEL_HEIGHT <= BOARD_GAME_PIXEL_HEIGHT)
			flag += 2;
		return flag;
	}
	
	/*
	 * Scan throught the unit list and return the unit if it's coordination is same with the given one 
	 */
	private Unit getUnitByCoord(Point coord) {
		for(int i = 0; i < map.getUnitsList().size(); i++) {
			if(map.getUnitsList().get(i).getUnitCoord().equals(coord) == true) {
				return map.getUnitsList().get(i);
			}
		}
		return null;
	}
	
	/*
	 * Construct move path in terms of pixel position
	 */
	private void constructMovePath(ArrayList<Point> path) {
		if(movePath == null)
			movePath = new ArrayList<Point>();
		else
			movePath.clear();
		
		int index = 0;
		Point coord;
		Point nxtCoord;
		while(true) {
			coord = path.get(index);
			
			if(index == path.size() - 1)
				return;
			else
				nxtCoord = path.get(index + 1);
			
			if(coord.x == nxtCoord.x && coord.y - 1 == nxtCoord.y) {	// down -> up
				int count = 0;
				while(count < BLOCK_PIXEL_HEIGHT) {
					movePath.add(new Point(path.get(index).x * BLOCK_PIXEL_WIDTH, path.get(index).y * BLOCK_PIXEL_HEIGHT - count));
					count++;
				}
			}
			else if(coord.x == nxtCoord.x && coord.y + 1 == nxtCoord.y) {	// up -> down
				int count = 0;
				while(count < BLOCK_PIXEL_HEIGHT) {
					movePath.add(new Point(path.get(index).x * BLOCK_PIXEL_WIDTH, path.get(index).y * BLOCK_PIXEL_HEIGHT + count));
					count++;
				}
			}
			else if(coord.x + 1 == nxtCoord.x && coord.y == nxtCoord.y) {	// left -> right
				int count = 0;
				while(count < BLOCK_PIXEL_WIDTH) {
					movePath.add(new Point(path.get(index).x * BLOCK_PIXEL_WIDTH + count , path.get(index).y * BLOCK_PIXEL_HEIGHT));
					count++;
				}
			}
			else if(coord.x - 1 == nxtCoord.x && coord.y == nxtCoord.y) {	// right -> left
				int count = 0;
				while(count < BLOCK_PIXEL_WIDTH) {
					movePath.add(new Point(path.get(index).x * BLOCK_PIXEL_WIDTH - count , path.get(index).y * BLOCK_PIXEL_HEIGHT));
					count++;
				}
			}
			
			index++;
		}
	}
	
	/*
	 * Check if the attack animation is over, then destroyed animation can be launched.
	 * Also the destroyed unit will be removed from the unit list
	 */
	public class Destroyed implements Runnable {

		public void run() {
			while(true) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
				
				if(isAnimationSectionAttack) {
					continue;
				}
				else {	// Attack animation is over
					isAnimationSectionDestroyed = true;
					animationClockDestroyed = 0;
					if(destroyedUnitCoord != null)
						map.getUnitsList().remove(getUnitByCoord(destroyedUnitCoord));
					break;
				}
			}
		}
	}
	
	/*
	 * Event handler for modelPropertyChange events coming from the controller
	 */
	@SuppressWarnings("unchecked")
	public void modelPropertyChange(final PropertyChangeEvent evt) {
		System.out.println(this.getClass().toString() + ".modelPropertyChange() called -- " + 
				evt.getPropertyName() + ": " + evt.getOldValue().toString() + 
				" -> " + evt.getNewValue().toString());
		
		if(evt.getPropertyName().equals("map")) {
			map = new Map((Map) evt.getNewValue());
		}
		else if(evt.getPropertyName().equals("unit")) {
			/*
			 * Remove a destroyed unit
			 */
			/*
			Unit destroyedUnit = (Unit) evt.getNewValue();
			if(((Unit) evt.getNewValue()).isDestroyed()) {
				for(int i = 0; i < map.getUnitsList().size(); i++) {
					if(map.getUnitsList().get(i).getUnitCoord().equals(destroyedUnit.getUnitCoord())) {
						map.getUnitsList().remove(i);
						return;
					}
				}
			}
			*/
			
			/*
			 * Modify unit property
			 */
			Unit oldUnit = (Unit) evt.getOldValue();
			for(int i = 0; i < map.getUnitsList().size(); i++) {
				if(map.getUnitsList().get(i).getUnitCoord().equals(oldUnit.getUnitCoord())) {
					map.getUnitsList().remove(i);
					map.getUnitsList().add(new Unit((Unit) evt.getNewValue()));
				}
			}
		}
		else if(evt.getPropertyName().equals("reachableArea")) {
			reachableArea = (ArrayList<Point>) evt.getNewValue();
		}
		else if(evt.getPropertyName().equals("path")) {
			path = (ArrayList<Point>) evt.getNewValue();
		}
		else if(evt.getPropertyName().equals("attackableArea")) {
			attackableArea = (ArrayList<Point>) evt.getNewValue();
		}
		else if(evt.getPropertyName().equals("isDestSet")) {
			constructMovePath(path);
			if(!movePath.equals(new ArrayList<Point>())) {
				moveIter = 0;
				isAnimationSectionMove = true;
			}
		}
		else if(evt.getPropertyName().equals("round")) {
			round = (Integer) evt.getNewValue();
        	isAnimationSectionRound = true;
		}
		else if(evt.getPropertyName().equals("attackedUnitCoord")) {
			attackedUnitCoord = (Point) evt.getNewValue();
        	isAnimationSectionAttack = true;
		}
		else if(evt.getPropertyName().equals("destroyedUnitCoord")) {
			destroyedUnitCoord = (Point) evt.getNewValue();
			Thread destroyed = new Thread(new Destroyed());
			destroyed.start();
		}
	}
}
